package org.klout4j;

import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;

import org.json.simple.parser.JSONParser;

public class Klout {
    Logger logger = Logger.getLogger(getClass().getName());
    String apiKey;
    String rootUrl = "http://api.klout.com";
    int connectTimeout = -1;
    int readTimeout = -1;
    String userAgent = "klout4j/0.7";

    public Klout() {}
    
    public Klout(String apiKey) {
        this.apiKey = apiKey;
    }

    public void setApiKey(String apiKey) {
        this.apiKey = apiKey;
    }

    public String getRootUrl() {
        return rootUrl;
    }
    public void setRootUrl(String rootUrl) {
        this.rootUrl = rootUrl;
    }

    public int getConnectTimeout() {
        return connectTimeout;
    }
    public void setConnectTimeout(int connectTimeout) {
        this.connectTimeout = connectTimeout;
    }

    public int getReadTimeout() {
        return readTimeout;
    }
    public void setReadTimeout(int readTimeout) {
        this.readTimeout = readTimeout;
    }

    public String getUserAgent() {
        return userAgent;
    }
    public void setUserAgent(String userAgent) {
        this.userAgent = userAgent;
    }

    public KloutUser showUser(String twitterScreenName)
        throws KloutException
    {
        List<String> twitterScreenNames = new ArrayList<String>();
        twitterScreenNames.add(twitterScreenName);
        Map<String,KloutUser> users = showUsers(twitterScreenNames);
        if (users.isEmpty()) {
            throw new ProfileNotFoundException(twitterScreenName);
        }
        else {
            return users.entrySet().iterator().next().getValue();
        }
    }

    @SuppressWarnings("unchecked")
	public Map<String,KloutUser> showUsers(List<String> twitterScreenNames)
        throws KloutException
    {
        logger.fine("Showing users: " + twitterScreenNames);
        StringBuilder bld = new StringBuilder()
            .append(rootUrl)
            .append("/1/users/show.json?key=");
        try {
            bld.append(URLEncoder.encode(apiKey, "utf-8"));
        }
        catch (java.io.UnsupportedEncodingException e) {
            throw new RuntimeException(e);
        }
        bld.append("&users=");
        int count = 0;
        for (String twitterScreenName : twitterScreenNames) {
            if (count++ > 0) {
                bld.append(',');
            }
            try {
                bld.append(URLEncoder.encode(twitterScreenName, "utf-8"));
            }
            catch (java.io.UnsupportedEncodingException e) {
                throw new RuntimeException(e);
            }
        }
        if (logger.isLoggable(Level.FINE)) {
            logger.fine("Hitting URL: " + bld.toString());
        }
        
        URL url;
        try {
            url = new URL(bld.toString());
        }
        catch (java.net.MalformedURLException e) {
            throw new KloutException("Failed to construct URL", e);
        }
        
        HttpURLConnection conn;
        try {
            conn = getHttpURLConnection(url, "GET", null, false);
            if (conn.getResponseCode() != 200) {
                throw new KloutException("Failed to get profile detail for \"" +
                                         twitterScreenNames +
                                         "\", HTTP status: " +
                                         conn.getResponseCode() + " " +
                                         conn.getResponseMessage());
            }
        }
        catch (java.io.IOException e) {
            throw new KloutException("Failed to open connection", e);
        }

        Map<String,Object> attributes;
        InputStream inputStream = null;
        try {
            inputStream = conn.getInputStream();
            attributes = (Map<String,Object>)
                new JSONParser().parse(new InputStreamReader(inputStream));
        }
        catch (Exception e) {
            throw new KloutException("Failed to read/parse response", e);
        }
        finally {
            if (inputStream != null) {
                try {
                    inputStream.close();
                }
                catch (Exception e) {
                    logger.log(Level.SEVERE,
                               "Failed to close HTTP InputStream",
                               e);
                }
            }
        }
        
        Object status = attributes.get("status");
        if (status == null) {
            throw new KloutException("No status element found");
        }
        else if (!"200".equals(status.toString())) {
            throw new ProfileNotFoundException("Profile for " +
                                               twitterScreenNames +
                                               " not found, status=" +
                                               status + " (" +
                                               attributes.get("status_message") +
                                               ")");
        }

        Map<String,KloutUser> usersByTwitterScreenName =
            new LinkedHashMap<String,KloutUser>();
        @SuppressWarnings("unchecked")
		List<Map<String,Object>> userAttrList = (List<Map<String,Object>>)attributes.get("users");
        if (userAttrList != null) {
            for (Map<String,Object> userAttrs : userAttrList) {
                KloutUser user = new KloutUser(userAttrs);
                usersByTwitterScreenName.put(user.getTwitterScreenName(), user);
            }
        }
        return usersByTwitterScreenName;
    }

    private HttpURLConnection getHttpURLConnection(URL url,
                                                   String requestMethod,
                                                   Map<String,String> headers,
                                                   boolean doOutput)
        throws java.io.IOException
    {
        HttpURLConnection conn = (HttpURLConnection)url.openConnection();
        if (connectTimeout != -1) {
            conn.setConnectTimeout(connectTimeout);
        }
        if (readTimeout != -1) {
            conn.setReadTimeout(readTimeout);
        }
        conn.setAllowUserInteraction(false);
        conn.setRequestMethod(requestMethod);
        conn.setDoInput(true);
        conn.setDoOutput(doOutput);
        conn.setRequestProperty("Connection", "close");
        conn.setRequestProperty("User-Agent", userAgent);
        if (headers != null) {
            for (Map.Entry<String,String> header : headers.entrySet()) {
                conn.setRequestProperty(header.getKey(),
                                        header.getValue());
            }
        }
        return conn;
    }
}
